package com.skyline.energy.definition;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import net.sf.cglib.core.ReflectUtils;

import org.apache.commons.lang.ClassUtils;

import com.skyline.energy.annotation.MapperBy;
import com.skyline.energy.annotation.Query;
import com.skyline.energy.annotation.Unique;
import com.skyline.energy.dataaccess.jdbc.RowMapper;
import com.skyline.energy.exception.DaoGenerateException;
import com.skyline.energy.utils.CommonUtils;
import com.skyline.energy.utils.GenericUtils;

public class JdbcQueryDefinition extends BaseJdbcDefinition {
	private int fetchSize;
	private int pageIndex = -1;
	private boolean isUnique = false;
	private RowMapper<?> rowMapper;
	
	public JdbcQueryDefinition(Method method) throws DaoGenerateException {
		init(method);
	}

	private void init(Method method) throws DaoGenerateException {
		configUnique(method);

		configRowMapper(method);

		Query query = method.getAnnotation(Query.class);
		fetchSize = query.fetchSize();

		Map<String, Integer> paramIndexes = new HashMap<String, Integer>(8, 1f);

		Class<?>[] paramTypes = method.getParameterTypes();
		Annotation[][] annotations = method.getParameterAnnotations();
		for (int index = 0; index < annotations.length; index++) {
			if (CommonUtils.isTypePage(paramTypes[index])) {
				pageIndex = index;
				break;
			}
		}
		
		parseParameterAnnotations(annotations, paramIndexes, null, paramTypes);

		String sql = query.value();
		parseSql(sql, paramTypes, paramIndexes);
		
		parseShardBy(method, paramIndexes, paramTypes);
	}

	private void configRowMapper(Method method) throws DaoGenerateException {
		MapperBy mapperBy = method.getAnnotation(MapperBy.class);
		Class<? extends RowMapper<?>> mapperType = null;
		if (mapperBy == null) {
			throw new DaoGenerateException("Missing @MapperBy on @Query");
		} else {
			mapperType = mapperBy.value();
		}
		rowMapper = (RowMapper<?>) ReflectUtils.newInstance(mapperType);

		Class<?> returnType = method.getReturnType();
		if (isUnique) {
			Class<?> expectedType = GenericUtils.getGenericTypeOfRowMapper(mapperType);
			if (!ClassUtils.isAssignable(returnType, expectedType, true)) {
				throw new DaoGenerateException("Return type is expected as '" + expectedType.getName()
						+ "' with @Unique, but actually is '" + returnType.getName() + "' in method: "
						+ method.toString() + ", missing @MappedBy");
			}
		} else {
			if (!CommonUtils.isTypeList(returnType)) {
				throw new DaoGenerateException(
						"Return type is expected as 'java.util.List' without @Unique, but actually is '"
								+ returnType.getName() + "' in method: " + method.toString());
			}
		}
	}

	private void configUnique(Method method) {
		Unique unique = method.getAnnotation(Unique.class);
		if (unique != null) {
			isUnique = true;
		} else {
			isUnique = false;
		}
	}

	public int getFetchSize() {
		return fetchSize;
	}

	public int getPageIndex() {
		return pageIndex;
	}

	public boolean isUnique() {
		return isUnique;
	}

	public RowMapper<?> getRowMapper() {
		return rowMapper;
	}
	

}
